#include <string.h>
#include <pthread.h>
#include <wget.h>
#include <fcntl.h>
#include <errno.h>

void *http_request(const char *method, const char *uri,
    const char *post_data, size_t data_len, void *user_data)
{
  void *res = NULL;
  wget_http_connection *conn = NULL;
  wget_http_request *req;
  wget_http_response *resp;
  wget_iri *iri;

  iri = wget_iri_parse(uri, NULL);
  req = wget_http_create_request(iri, method);
  wget_http_add_header(req, "Connection", "keepalive");
  if (post_data) {
   // printf("[DEBUG] [L%d f-%s] body:%s\n", __LINE__, __func__, post_data);
    //wget_http_request_set_body(req, "application/x-www-form-urlencoded",
    wget_http_request_set_body(req, "application/json",
        wget_memdup(post_data, data_len), data_len);
  }
  wget_http_open(&conn, iri);
  if (conn) {
    if (!wget_http_send_request(conn, req)) {
      resp = wget_http_get_response(conn);
      if (resp && (resp->body != NULL)) {
        res = calloc(1, resp->body->length+2);
        memcpy(res, resp->body->data, resp->body->length);
        wget_http_free_response(&resp);
      }
    }
  }
  wget_http_free_request(&req);
  wget_iri_free(&iri);
  wget_http_close(&conn);
//  wget_global_deinit();
  //printf("[DEBUG]L%d f-%s res:%s\n", __LINE__, __func__, (char *)res);

  return res;
}
void *http_request_digest(const char *method, const char *uri,
    const char *post_data, size_t data_len, size_t *size_p,
    const char *username, const char *password)
{
  void *res = NULL;
  wget_http_connection *conn = NULL;
  wget_http_request *req;
  wget_http_response *resp;
  wget_iri *iri;

  *size_p = 0;
  //printf("[DEBUG]L%d f-%s method:%s url:%s\n", __LINE__, __func__, method, uri);
  wget_global_init(
     // WGET_DEBUG_STREAM, stderr, WGET_ERROR_STREAM, stderr,
      //WGET_INFO_STREAM, stdout,
      0);
  wget_tcp_set_timeout(NULL, 5000);
  iri = wget_iri_parse(uri, NULL);
  req = wget_http_create_request(iri, method);
  wget_http_add_header(req, "Connection", "keep-alive");
  wget_http_add_header(req, "Accept", "*/*");
  wget_http_add_header(req, "Accept-Encoding", "gzip");
  if (post_data)
    //wget_http_request_set_body(req, "application/x-www-form-urlencoded",
    wget_http_request_set_body(req, "application/json",
        wget_memdup(post_data, data_len), data_len);
  wget_http_open(&conn, iri);
  if (conn) {
    if (!wget_http_send_request(conn, req)) {
      resp = wget_http_get_response(conn);
      if (resp) {
        wget_http_add_credentials(req,
            wget_vector_get(resp->challenges, 0),
            username, password, 0);
        wget_http_free_response(&resp);
        if (!wget_http_send_request(conn, req)) {
          resp = wget_http_get_response(conn);
          if (resp) {
            res = calloc(1, resp->body->length);
            memcpy(res, resp->body->data, resp->body->length);
            *size_p = resp->body->length;
            wget_http_free_response(&resp);
          }
        }
      }
    }
  }
  //wget_http_free_challenge(challenge);
  wget_http_free_request(&req);
  wget_iri_free(&iri);
  wget_http_close(&conn);
  wget_global_deinit();
  //printf("[DEBUG]L%d f-%s res:%s\n", __LINE__, __func__, (char *)res);

  return res;
}

//libwebsockets
/* test
 * wget "0.0.0.0:7681/form1?text1=ss&send=uu" -O tmp.txt
 * wget --method=POST 0.0.0.0:7681/forsm1 --body-data="text1=value1&send=value2"  -O tmp.txt
 */
#include <string.h>
#include <stdio.h>
#include <signal.h>
#define RING_DEPTH 4096
/*
struct msg {
        void *payload; // is malloc'd 
        size_t len;
        char binary;
        char first;
        char final;
};
struct self {
  int flag;
  void *userdata;

  //http
  struct lws_spa *spa;
  //websocket
        struct lws_ring *ring;
  char *url;
  int urlLen;
  char *body;
  int bodyLen;
        uint32_t msglen;
        uint32_t tail;
        uint8_t completed:1;
        uint8_t flow_controlled:1;
        uint8_t write_consume_pending:1;
};
static int interrupted;
enum enum_param_names {
  EPN_TEXT1,
  EPN_SEND,
};
//static const char *server_host = "0.0.0.0:7681";
//int port = 7681;
static lws_sorted_usec_list_t sul;
struct lws_context *context;
static struct lws *client_wsi;
static const char *server_address = "0.0.0.0",
             *pro = "lws-mirror-protocol";

bool ends_with(const char *str, const char *suffix)
{
  size_t len_str = strlen(str);
  size_t len_suffix = strlen(suffix);

  if (len_suffix > len_str) return false;
  return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0;
}

bool is_file_request(const char *url)
{
  const char *file_extensions[] = {".ico", ".html", ".jpg", ".png", ".css", ".js", ".json", ".pdf", ".txt", NULL};

  for (int i = 0; file_extensions[i] != NULL; i++) {
    if (ends_with(url, file_extensions[i])) {
      return true;
    }
  }
  return false;
}
int http_get(struct lws *wsi, void *url, void *user)
{
  printf("[DEBUG] f-%s\n", __func__);
  int n, m;
  uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE + 4096000],
  *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1];
  const struct lws_protocols *protocols = lws_get_protocol(wsi);
  int urlLen = strlen(url);

  if (is_file_request(url)) {
    printf("[DEBUG] L%d is file request\n", __LINE__);
    if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
    //if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
          (unsigned char *)url, urlLen, &p, end) < 0)
      return -1;
  } else {
    printf("[DEBUG] L%d isn't file request\n", __LINE__);
    //lws_return_http_status(wsi, HTTP_STATUS_OK, "oktest");
    if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
          "text/html", LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
      return 1;
    if (lws_finalize_write_http_header(wsi, start, &p, end))
      return 1;
      char *result = NULL;//"no handle";
      if (protocols) {
        //printf("ptr:%p\n", protocols->user);
        http_handle handle = protocols->user;
        result = (char *)handle("get", url+1, urlLen-1);
        if (result) {
          printf("result len:%ld\n", strlen(result));
          if (strlen(result) > (sizeof(buf) - LWS_PRE)) {
            n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE,
                "The data is too large:%ld", strlen(result));
          } else {
            n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "%s",
                result);
          }
          free(result);
        }
      }
    m = lws_write(wsi, p, n, LWS_WRITE_HTTP_FINAL);
    if (m < n) {
      lwsl_err("ERROR %d writing to socket\n", n);
      return -1;
    }
    if (lws_http_transaction_completed(wsi))
      return -1;
    return 0;
  }
  return 0;
}
int http_post_callback(struct lws *wsi, void *in, size_t len,
    void *user)
{
  char *result = NULL;
  uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
  *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1];
  struct self *self = (struct self *)user;
  const struct lws_protocols *protocols = lws_get_protocol(wsi);
  printf("self flag:%d\n", self->flag);
  char *url = self->url, *body = self->body;
  int urlLen = self->urlLen, bodyLen = self->bodyLen;
  //printf("in:%s. len:%d.\n", in, len);
  printf("url:%s\n, body:%.*s\n", self->url, self->bodyLen, self->body);
  if (strncmp(url, "/update", urlLen) == 0) {
    if (protocols) {
      http_handle handle = protocols->user;
      result = (char *)handle(url+1, body, bodyLen);
    }
  }
  if (result == NULL) {
   if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
          (unsigned char *)"db.html", 7, &p, end) < 0)
      return -1;
  } else {
    return 0;
  }
    return 0;
}
static int callback_http(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
  struct self *self = (struct self *)user;
  int rc;

  printf("%d-", reason);
  if (len)
    printf("in:%.*s len:%ld\n", (int)len, (char *)in, len);
  switch (reason) {
  case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
    printf("[++++ DEBUG] [LWS_CALLBACK_FILTER_HTTP_CONNECTION]%d\
        http url:%s\n", __LINE__, (const char *)in);
    break;
  case LWS_CALLBACK_HTTP:
    printf("[DEBUG] [LWS_CALLBACK_HTTP]\n");
    self->url = in;
    self->urlLen = len;
    char url[1024];
    snprintf(url, sizeof(url), "%s", (char *)in);
    if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
      rc = http_get(wsi, url, user);
      return rc;
    } else if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
      return 0;
    }
    break;
  case LWS_CALLBACK_HTTP_BODY:
    printf("[DEBUG] [LWS_CALLBACK_HTTP_BODY]\n");
    self->body = in;
    self->bodyLen = len;
    if (!self->spa) {
      self->spa = lws_spa_create(wsi, param_names,
          LWS_ARRAY_SIZE(param_names), 1024,
          NULL, NULL); 
      if (!self->spa)
        return -1;
    }
    if (lws_spa_process(self->spa, in, (int)len))
      return -1;
    break;

  case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
    printf("[DEBUG] [LWS_CALLBACK_CLOSED_CLIENT_HTTP]\n");
    if (self->spa && lws_spa_destroy(self->spa))
      return -1;
    break;

  case LWS_CALLBACK_RECEIVE:
    printf("[DEBUG] [LWS_CALLBACK_RECEIVE]\n");
    printf("Received data: %.*s\n", (int)len, (char *)in);
    break;
  case LWS_CALLBACK_HTTP_BODY_COMPLETION:
    lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
    lws_spa_finalize(self->spa);
    http_post_callback(wsi, in, len, user);
    break;
  case LWS_CALLBACK_HTTP_WRITEABLE:
    printf("writeable\n");
    break;

  case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
    if (self->spa) {
      lws_spa_destroy(self->spa);
      self->spa = NULL;
    }
    break;
  default:
    break;
  }
  return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static void connect_cb(lws_sorted_usec_list_t *_sul)
{
  struct lws_client_connect_info i;

  lwsl_notice("%s: connecting\n", __func__);
  memset(&i, 0, sizeof(i));
  i.context = context;
  i.address = server_address;
  i.origin = i.address;
  i.protocol = pro;
  i.alpn = "h2;http/1.1";
  i.local_protocol_name = "lws-ping-test";
  i.pwsi = &client_wsi;
  i.retry_and_idle_policy = &retry;

  if (!lws_client_connect_via_info(&i))
          lws_sul_schedule(context, 0, _sul, connect_cb,
  5 * LWS_USEC_PER_SEC);
}
static int callback_websocket(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
  printf("L%d reason:%d\n", __LINE__, reason);
        switch (reason) {
    case LWS_CALLBACK_CLIENT_ESTABLISHED:
      lwsl_warn("LWS_CALLBACK_ESTABLISHED\n");
      break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:
      lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n");
      break;
    case LWS_CALLBACK_CLIENT_RECEIVE:
      printf("Received data: %.*s\n", (int)len, (char *)in);
      break;
    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
      lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
         in ? (char *)in : "(null)");
      lws_sul_schedule(context, 0, &sul, connect_cb,
          5 * LWS_USEC_PER_SEC);
      break;

    case LWS_CALLBACK_CLOSED:
      printf("L%d\n", __LINE__);
      lwsl_user("LWS_CALLBACK_CLOSED\n");
      break;
    default:
      break;
        }
       return 0;
}

void *http_ws_httpd_run(void *ptr)
{
  printf("%s ptr:%p\n", __func__, ptr);
  http_handle handle = ptr;
  int port = (int)(intptr_t)handle("getPort", NULL, 0);
  const char *origin = (char *)handle("getOrigin", NULL, 0);
  if (!origin) {
    printf("[ERROR] no origin\n");
    exit(1);
  }
  struct lws_context_creation_info info;
  int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;// | LLL_DEBUG;

  struct lws_protocols protocols[] = {
    { "http", callback_http, sizeof(struct self), 0, .user = ptr },
    { "websocket", callback_websocket, sizeof(struct self), 0 },
    { NULL, NULL, 0, 0 }
  };
  struct lws_http_mount mount = { NULL, "/", origin, "index.html",
    NULL, NULL, NULL, NULL,
    0, 0, 0, 0, 0, 0,
    LWSMPRO_FILE, 1, NULL,
  };
  lws_set_log_level(logs, NULL);
  printf("[DEBUG] LWS visit http://localhost:%d origin:%s\n",
      port, origin);

  memset(&info, 0, sizeof info);
  info.port = port;
  info.protocols = protocols;
  info.mounts = &mount;
  info.options =
    LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
  info.user = ptr;

  context = lws_create_context(&info);
  if (!context) {
    lwsl_err("lws init failed\n");
    return NULL;
  }
  lws_sul_schedule(context, 0, &sul, connect_cb, 100);
  while (n >= 0 && !interrupted)
    n = lws_service(context, 0);

  lws_context_destroy(context);
  return NULL;
}

void *http_ws_httpd_thread(void *ptr)
{
  pthread_t thread;
  pthread_create(&thread, NULL, http_ws_httpd_run, ptr);
  pthread_detach(thread);
  return NULL;
}
*/
#include "http.h"
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
#include <libxml/parser.h>
void *http__init(int argc, const char * const *argv)
{
  struct http_struct *self = NULL;
  xmlDocPtr doc;
  xmlNodePtr root_node, now_node;
  xmlChar *xml_char = NULL;
  const char *filename;

  if ((argc == 3) && !strcmp(argv[1], "-c")) {
    filename = argv[2];
  } else {
    printf("Usage: %s [OPTION]... [FILE]...\n\
%s\n\
-c\tconfig file\n\
Examples:\n\
\t%s -c config.xml\n", "http", "http", "http");
    exit(1);
  }
  if (access(filename, F_OK) == 0) {
    doc = xmlParseFile(filename);
    printf("[DEBUG]xml_driver__file:%s\n", filename);
  } else {
    printf("[----ERROR] no %s\n", filename);
    exit(1);
   // return NULL;
  }
  self = calloc(1, sizeof(*self));
  root_node = xmlDocGetRootElement(doc);
  now_node = xmlFirstElementChild(root_node);
  now_node = xmlFirstElementChild(now_node);
  xml_char = xmlGetProp(now_node, BAD_CAST "httpPort");
  if (xml_char) {
    self->port = atoi((char *)xml_char);
    xmlFree(xml_char);
  }
  xml_char = xmlGetProp(now_node, BAD_CAST "httpOrigin");
  if (xml_char) {
    self->origin = strdup((char *)xml_char);
    xmlFree(xml_char);
  }
  //printf("[DEBUG] self:%p\n", self);
  return self;
}
bool http__ends_with(const char *str, const char *suffix)
{
  size_t len_str = strlen(str);
  size_t len_suffix = strlen(suffix);

  if (len_suffix > len_str) return false;
  return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0;
}
bool http__is_file_request(const char *url)
{
  const char *file_extensions[] = {".ico", ".html", ".jpg", ".png", ".css", ".js", ".json", ".pdf", ".txt", NULL};

  for (int i = 0; file_extensions[i] != NULL; i++) {
    if (http__ends_with(url, file_extensions[i])) {
      return true;
    }
  }
  return false;
}
int http__get(struct lws *wsi, void *url, void *user)
{
  PRINTF_FUNC;
  struct http_struct *self = user;
  int n, m;
  uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE + 4096000],
  *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1];
  const struct lws_protocols *protocols = lws_get_protocol(wsi);
  int urlLen = strlen(url);

  if (http__is_file_request(url)) {
    printf("[DEBUG] L%d is file request\n", __LINE__);
    if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
    //if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
          (unsigned char *)url, urlLen, &p, end) < 0)
      return -1;
  } else {
    printf("[DEBUG] L%d isn't file request\n", __LINE__);
    //lws_return_http_status(wsi, HTTP_STATUS_OK, "oktest");
    if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
          "text/html", LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
      return 1;
    if (lws_finalize_write_http_header(wsi, start, &p, end))
      return 1;
    char *result = NULL;
    http_handle handle = self->handle;
    if (protocols) {
      //printf("ptr:%p\n", protocols->user);
      result = (char *)handle(self->parent, url, NULL, urlLen);
      if (result) {
        printf("result len:%ld\n", strlen(result));
        if (strlen(result) > (sizeof(buf) - LWS_PRE)) {
          n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE,
              "The data is too large:%ld", strlen(result));
        } else {
          n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "%s",
              result);
        }
        free(result);
      }
    }
    if (result) {
      m = lws_write(wsi, p, n, LWS_WRITE_HTTP_FINAL);
      if (m < n) {
        lwsl_err("ERROR %d writing to socket\n", n);
        return -1;
      }
    }
    if (lws_http_transaction_completed(wsi))
      return -1;
    return 0;
  }
  return 0;
}
static void http__connect_cb(struct lws_sorted_usec_list *ptr)
{
  PRINTF_FUNC;
  struct http_struct *self = (struct http_struct *)ptr;
  //lws_sorted_usec_list_t *_sul = self->_sul;
  struct lws_client_connect_info i;

  memset(&i, 0, sizeof(i));
  i.context = self->context;
  i.address = self->server_address;
  i.origin = i.address;
  i.protocol = self->pro;
  i.alpn = "h2;http/1.1";
  i.local_protocol_name = "lws-ping-test";
  i.pwsi = &(self->client_wsi);
  i.retry_and_idle_policy = self->_retry;

  if (!lws_client_connect_via_info(&i))
    lws_sul_schedule(self->context, 0,
        (struct lws_sorted_usec_list *)self, http__connect_cb,
        5 * LWS_USEC_PER_SEC);
}
static int
file_upload_cb(void *data, const char *name, const char *filename,
         char *buf, int len, enum lws_spa_fileupload_states state)
{
  struct http_struct *self = data;

  switch (state) {
  case LWS_UFS_OPEN:
    /* take a copy of the provided filename */
    lws_strncpy(self->filename, filename, sizeof(self->filename) - 1);
    /* remove any scary things like .. */
    lws_filename_purify_inplace(self->filename);
    /* open a file of that name for write in the cwd */
    self->fd = lws_open(self->filename, O_CREAT | O_TRUNC | O_RDWR, 0600);
    if (self->fd == -1) {
      lwsl_notice("Failed to open output file %s\n",
            self->filename);
      return 1;
    }
    break;
  case LWS_UFS_FINAL_CONTENT:
  case LWS_UFS_CONTENT:
    if (len) {
      int n;

      self->file_length += len;

      n = write(self->fd, buf, len);
      if (n < len) {
        lwsl_notice("Problem writing file %d\n", errno);
      }
    }
    if (state == LWS_UFS_CONTENT)
      /* wasn't the last part of the file */
      break;

    /* the file upload is completed */

    lwsl_user("%s: upload done, written %ld to %s\n", __func__,
        self->file_length, self->filename);

    close(self->fd);
    self->fd = -1;
    break;
  case LWS_UFS_CLOSE:
    break;
  }

  return 0;
}

static int http__callback_http(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
  const struct lws_protocols *protocols = lws_get_protocol(wsi);
  struct http_struct *self = NULL;
  //uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
  uint8_t buf[LWS_PRE + 1024000],//LWS_RECOMMENDED_MIN_HEADER_SPACE],
  *start = &buf[LWS_PRE], *p = start,
  *end = p + sizeof(buf) - LWS_PRE;
  size_t m, n = 0;
  char *result = NULL;
  if (protocols)
    self = protocols->user;
  //printf("L%d [DEBUG] self:%p\n", __LINE__, self);
  int rc;

  printf("%d-", reason);
  if (len)
    printf("in:%.*s.len:%ld.\n", (int)len, (char *)in, len);
  switch (reason) {
  case LWS_CALLBACK_HTTP:
    printf("[DEBUG] [LWS_CALLBACK_HTTP]\n");
    self->url = strndup(in, len);
    self->urlLen = len;
    char url[1024];
    snprintf(url, sizeof(url), "%s", (char *)in);
    if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
      rc = http__get(wsi, url, self);
      return rc;
    } else if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
      return 0;
    }
    break;
  case LWS_CALLBACK_HTTP_BODY:
    printf("[DEBUG] [LWS_CALLBACK_HTTP_BODY]\n");
    self->body = strndup(in, len);
    self->bodyLen = len;
    if (!self->spa) {
      void *p;
      const char *const *param_names = NULL;
      int param_names_size;
      self->set_param_names(self->url, &p,
          &param_names_size);
      param_names = p;
      if (!strcmp(self->url, "uploadFiles")) {
        self->spa = lws_spa_create(wsi, param_names,
            param_names_size, 1024,
            file_upload_cb, self);
      } else {
        self->spa = lws_spa_create(wsi, param_names,
            param_names_size, 1024,
            NULL, NULL);
      }
      if (!self->spa)
        return -1;
    }
    if (lws_spa_process(self->spa, in, (int)len))
      return -1;
    break;

  case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
    printf("[DEBUG] [LWS_CALLBACK_CLOSED_CLIENT_HTTP]\n");
    break;

  case LWS_CALLBACK_RECEIVE:
    printf("[DEBUG] [LWS_CALLBACK_RECEIVE]\n");
    printf("Received data: %.*s\n", (int)len, (char *)in);
    break;
  case LWS_CALLBACK_HTTP_BODY_COMPLETION:
    lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
    lws_spa_finalize(self->spa);
      const char *const *param_names = NULL;
      int param_names_size = 0;
      /*
      self->set_param_names(self->url, &param_names,
          &param_names_size);
          */
    //for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) {
    for (n = 0; n < param_names_size; n++) {
      if (!lws_spa_get_string(self->spa, n))
        lwsl_user("%s: undefined\n", param_names[n]);
      else
        lwsl_user("%s: (len %d) '%s'\n",
            param_names[n],
            lws_spa_get_length(self->spa, n),
            lws_spa_get_string(self->spa, n));
    }

    http_handle handle = self->handle;
    if (handle) {
      result = (char *)handle(self->parent, self->url,
          lws_spa_get_string(self->spa, 0),
          lws_spa_get_length(self->spa, 0));
    }
    if (result)
      n = strlen(result);
    if (n > (sizeof(buf) - LWS_PRE)) {
      char tmp[1024];
      snprintf(tmp, sizeof(tmp), "too large :%ld buf size:%ld\n",
          n, sizeof(buf) - LWS_PRE);
      result = strdup(tmp);
      n = strlen(result);
    }
    if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
      "text/html", n, &p, end))
      return 1;
    if (lws_finalize_write_http_header(wsi, start, &p, end))
      return 1;
    if (result) {
      n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "%s",
          result);
      free(result);
    }
    m = lws_write(wsi, (uint8_t *)p, n, LWS_WRITE_HTTP_FINAL);
    if (m < n) {
      lwsl_err("ERROR %ld writing to socket\n", n);
      return -1;
    } else {
      //printf("[DEBUG] writed :%ld %s\n", m, p);
      printf("[----] http:%ld\n", m);
    }
    if (lws_http_transaction_completed(wsi))
      return -1;
    break;

    if (lws_http_transaction_completed(wsi))
      return -1;
    break;
  case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
    if (self->spa) {
      lws_spa_destroy(self->spa);
      self->spa = NULL;
    }
    break;
  default:
    break;
  }
  return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static int http__callback_websocket(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
  struct http_struct *self = user;
  //printf("L%d reason:%d\n", __LINE__, reason);
  printf("%d+", reason);
  switch (reason) {
    case LWS_CALLBACK_CLIENT_ESTABLISHED:
      lwsl_warn("LWS_CALLBACK_ESTABLISHED\n");
      break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:
      lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n");
      break;
    case LWS_CALLBACK_CLIENT_RECEIVE:
      printf("Received data: %.*s\n", (int)len, (char *)in);
      break;
    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
      lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
         in ? (char *)in : "(null)");
      lws_sul_schedule(self->context, 0, self->_sul, http__connect_cb,
          5 * LWS_USEC_PER_SEC);
      break;

    case LWS_CALLBACK_CLOSED:
      printf("L%d\n", __LINE__);
      lwsl_user("LWS_CALLBACK_CLOSED\n");
      break;
    default:
      break;
        }
       return 0;
}
void *http__ws_httpd__run(void *ptr)
{
  PRINTF_FUNC;
  struct http_struct *self = ptr;
  int port = self->port;
  const char *origin = self->origin;
  //static lws_sorted_usec_list_t sul;

  if (!origin) {
    printf("[ERROR] no origin\n");
    exit(1);
  }
  static const char * const param_names[] = {
    "text1",
    "send",
  };
  static const lws_retry_bo_t retry = {
    .secs_since_valid_ping = 3,
    .secs_since_valid_hangup = 10,
  };
  self->_retry = &retry;
  self->param_names = param_names;
  struct lws_context_creation_info info;
  int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;// | LLL_DEBUG;

  struct lws_protocols protocols[] = {
  { "http", http__callback_http, sizeof(struct http_struct),
    0, .user = ptr },
  { "websocket", http__callback_websocket, sizeof(struct http_struct), 0 },
  { NULL, NULL, 0, 0 } /* terminator */
};
struct lws_http_mount mount = {
  /* .mount_next */              NULL,            /* linked-list "next" */
  /* .mountpoint */               "/",            /* mountpoint URL */
  /* .origin */           origin,       /* serve from dir */
  //           "./wsdb/mount-origin",      
  /* .def */                      "index.html",   /* default filename */
  /* .protocol */                 NULL,
  /* .cgienv */                   NULL,
  /* .extra_mimetypes */          NULL,
  /* .interpret */                NULL,
  /* .cgi_timeout */              0,
  /* .cache_max_age */            0,
  /* .auth_mask */                0,
  /* .cache_reusable */           0,
  /* .cache_revalidate */         0,
  /* .cache_intermediaries */     0,
  /* .origin_protocol */          LWSMPRO_FILE,   /* files in a dir */
  /* .mountpoint_len */           1,              /* char count */
  /* .basic_auth_login_file */    NULL,
};
  lws_set_log_level(logs, NULL);
  printf("[DEBUG] LWS visit http://localhost:%d origin:%s.\n",
      port, origin);

  memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
  info.port = port;
  info.protocols = protocols;
  info.mounts = &mount;
  info.options =
    LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
  info.user = ptr;

  struct lws_context *context;
  context = lws_create_context(&info);
  if (!context) {
    lwsl_err("lws init failed\n");
    return NULL;
  }
  self->context = context;
  //lws_sul_schedule(context, 0, &sul, http__connect_cb, 100);
  //self->_sul = &sul;
  //printf("sul:%p\n", &sul);
  while (n >= 0 && !self->interrupted)
    n = lws_service(context, 0);

  lws_context_destroy(context);
  return NULL;
}
void *http__ws_httpd__thread(void *ptr)
{
  pthread_t thread;
  pthread_create(&thread, NULL, http__ws_httpd__run, ptr);
  pthread_detach(thread);
  return NULL;
}
